/**
 * Copyright (c) 2006 Amgad S Youssef
 * All Rights Reserved.
 * Licensed under the Eclipse Public License - v 1.0
 * For more information see http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipsejcde.core.launchers;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipsejcde.core.Activator;

/**
 * @author Amgad S Youssef
 *
 */
public abstract class AbstractLauncher implements ILauncher {
	public static final String DEFAULT_LAUNCH_CONFIGURATION_NAME = "Default launcher";

	protected ILaunch launched;

	@Override
	public IStatus launch(String arguments) {
		return launch(arguments, DEFAULT_LAUNCH_CONFIGURATION_NAME, DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	@Override
	public IStatus launch(String arguments, String launchConfigurationName) {
		return launch(arguments, launchConfigurationName, DELETE_MODE_DO_NOT_DELETE);
	}

	@Override
	public IStatus launch(String arguments, String launchConfigurationName, int deleteMode) {
		return launch(arguments, launchConfigurationName, false, deleteMode);
	}

	@Override
	public IStatus launch(String arguments, String launchConfigurationName, int launchMode, int deleteMode) {
		return launch(arguments, launchConfigurationName, launchMode, false, deleteMode);
	}

	@Override
	public IStatus launch(String arguments, String launchConfigurationName, boolean waitForTerminatedSignal, int deleteMode) {
		return launch(arguments, launchConfigurationName, LAUNCH_MODE_RUN, waitForTerminatedSignal, deleteMode);
	}

	private IStatus launch(String arguments, String launchConfigurationName, int launchMode, boolean waitForTerminatedSignal, int deleteMode) {
		if (!isTerminated()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, launchConfigurationName + " is already running", null);
		}
		try {
			ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
			ILaunchConfiguration[] launchConfigs = manager.getLaunchConfigurations();
			for (int i = 0; i < launchConfigs.length; i++) {
				if (launchConfigs[i].getName().equalsIgnoreCase(launchConfigurationName)) {
					launchConfigs[i].delete();
				}
			}
			ILaunchConfigurationType launchConfigType = manager.getLaunchConfigurationType(getLaunchConfigurationTypeID());
			ILaunchConfigurationWorkingCopy workingCopy = launchConfigType.newInstance(null, launchConfigurationName);
			Map<?, ?> attributesMap = getLaunchConfigurationAttributes(arguments);
			Iterator<?> iter = attributesMap.keySet().iterator();
			while (iter.hasNext()) {
				String attributeName = (String) iter.next();
				Object obj = attributesMap.get(attributeName);
				if (obj instanceof Boolean) {
					Boolean value = (Boolean) obj;
					workingCopy.setAttribute(attributeName, value.booleanValue());
				} else if (obj instanceof Integer) {
					Integer value = (Integer) obj;
					workingCopy.setAttribute(attributeName, value.intValue());
				} else if (obj instanceof String) {
					String value = (String) obj;
					workingCopy.setAttribute(attributeName, value);
				} else if (obj instanceof List) {
					List<?> value = (List<?>) obj;
					workingCopy.setAttribute(attributeName, value);
				} else if (obj instanceof Map) {
					Map<?, ?> value = (Map<?, ?>) obj;
					workingCopy.setAttribute(attributeName, value);
				}
			}

			//launch mode
			ILaunchConfiguration launchConfig = workingCopy.doSave();
			if (launchMode == LAUNCH_MODE_DEBUG) {
				launched = launchConfig.launch(ILaunchManager.DEBUG_MODE, null);
			} else if (launchMode == LAUNCH_MODE_RUN) {
				launched = launchConfig.launch(ILaunchManager.RUN_MODE, null);
			}
			
			//view console
			IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles();
			for (int i = 0; i < consoles.length; i++) {
				ConsolePlugin.getDefault().getConsoleManager().showConsoleView(consoles[i]);
			}
			
			//wait for terminated signal
			if (waitForTerminatedSignal) {
				while (!launched.isTerminated()) {
					Thread.sleep(10);
				}
				IProcess[] processes = launched.getProcesses();
				for (int i = 0; i < processes.length; i++) {
					if (processes[i].getExitValue() != 0) {
						return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, launchConfigurationName + " terminated with an error", null);
					}
				}
			}
			
			//delete mode
			if (deleteMode == DELETE_MODE_DELETE_AFTER_LAUNCH) {
				launchConfig.delete();
			} else if (deleteMode == DELETE_MODE_DELETE_AFTER_TERMINATE) {
				final ILaunchConfiguration finalLaunchConfig = launchConfig;
				new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							while (!isTerminated()) {
								Thread.sleep(100);
							}
						} catch (InterruptedException e) {
						}
						try {
							finalLaunchConfig.delete();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}).start();
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, launchConfigurationName + " started successfully", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public ILaunch getLaunched() {
		return launched;
	}

	@Override
	public IStatus terminate() {
		try {
			if (!isTerminated()) {
				launched.terminate();
				launched = null;
				return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, getLauncherName() + " stopped successfully", null);
			} else {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, getLauncherName() + " is already stopped", null);
			}
		} catch (DebugException e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	@Override
	public boolean isTerminated() {
		if (launched == null || launched.isTerminated()) {
			return true;
		} else {
			return false;
		}
	}

	protected abstract String getLaunchConfigurationTypeID() throws Exception;

	protected abstract Map<?, ?> getLaunchConfigurationAttributes(String arguments) throws Exception;

	protected abstract String getLauncherName();
}
